﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

using System.Globalization;
using System.Runtime.InteropServices;
using System.Security;
using System.Threading;

using System.Diagnostics;

namespace oled
{
    /// 取模方向 与 扫描模式共同作用
    class OLED_Align_Mode
    {
        public const bool Big_endian = false;    //大端对齐模式 顺向
        public const bool Little_endian = true;  //小端对齐模式 逆向
    }

    public enum OLED_Scan_Mode
    {
        //Vertical_scan = 0,          //垂直扫描(字节垂直,数据垂直)
        //Horizontal_scan = 1,        //水平扫描(字节水平,数据水平)
        VertiByte_HoriScan = 2, //列行式(字节垂直,数据水平)
        HoriByte_Vertiscan = 3, //行列式(字节水平,数据垂直)
    }
    public class OLED_BYTE
    {
        public int X;
        public int Y;
        public byte bytes;
        public bool[] points; 
        public OLED_BYTE(int x, int y)
        {
            X = x;
            Y = y;
        }
        public OLED_BYTE(int x, int y, byte data)
        {
            X = x;
            Y = y;
            bytes = data;
        }
        public OLED_BYTE(int x, int y, byte data, bool bit_order)
        {
            X = x;
            Y = y;
            bytes = data;
            points = Byte_order.Bool_Array(bytes, bit_order);
        }
    }
    public partial class OLED_Drive
    {

        public static int OLED_DPI = 3;
        public static int OLED_Height = 64;
        public static int OLED_Width = 128;
        public static bool Bit_Order = OLED_Align_Mode.Little_endian;
        public static bool PixelSytle = false;
        public static OLED_Scan_Mode Scan_Mode = OLED_Scan_Mode.VertiByte_HoriScan;

        public static bool[,] oled_map;
        //public  Rectangle[,] oled_Screen;
        //public  Rectangle[,] oled_Screen_buff1;

        #region 底层模拟 OLED绘制 

        //生成一个像素点
        private  Rectangle PixelPoint(int x, int y)
        {
            int PixelSize = OLED_DPI;

            if (PixelSytle == true)
            {
                PixelSize --;
            }
            x *= PixelSize;
            y *= PixelSize;

            return new Rectangle(new Point(x, y), new Size(PixelSize, PixelSize));
        }

        //一个字节 获取绘制点图
        private List<Rectangle> VH_GetPoints(OLED_BYTE oled)
        {
            List<Rectangle> PointList = new List<Rectangle>();
            //Rectangle[] Screen_buff = new Rectangle[8];

            if (oled.X >= OLED_Width || oled.Y >= OLED_Height) return PointList;
            PointList.Clear();

            for (int y = 0; y < 8; y++)
            {
                if (oled.points[y])
                {
                    Rectangle point = PixelPoint(oled.X, y + oled.Y);
                    //Screen_buff[y] = point;
                    PointList.Add(point);
                }
            }
            return PointList;
        }
        //map数据更新到oled上      
        public void MapTo_oled(Bitmap oled)
        {
            //Graphics Graphic = Graphics.FromImage(oled);
            byte[] bmp = new byte[OLED_Height * OLED_Width / 8];
            int cnt = 0;
            for (int row = 0; row < OLED_Height; row += 8)
            {
                bool[] data = new bool[8];
                for (int x = 0; x < OLED_Width; x++)
                {
                    for (int offset = 0; offset < 8; offset++)
                    {
                        data[offset] = oled_map[offset + row, x];
                    }
                    bmp[cnt] = Byte_order.ToByte(data, Bit_Order);
                    cnt++;
                }
            }
            RAM_Load(bmp,oled);
        }
        //字节数据映射到bitmap (called by ShowBMP)
        public void Byte_ToMap(int Pos_x, int Pos_y, Byte data, int offset_x)
        {
            bool[] point = Byte_order.Bool_Array(data, Bit_Order);
            int addr_column = Pos_x + offset_x;
            for (int bitx = 0; bitx < 8; bitx++)
            {
                int addr_row = Pos_y + bitx;
                if (addr_column >= OLED_Width || Pos_y + bitx >= OLED_Height) break;

                oled_map[addr_row, addr_column] = point[bitx];
            }
        }
        //字节数据映射到bitmap (called by BMP_to_map)
        public void Byte_ToMap(int Pos_x, int Pos_y, Byte data)
        {
            bool[] point = Byte_order.Bool_Array(data, Bit_Order);
            for (int bitx = 0; bitx < 8; bitx++)
            {
                if (Pos_x >= OLED_Width || Pos_y + bitx >= OLED_Height) break;
                oled_map[Pos_y + bitx, Pos_x] = point[bitx];
            }
        }
        //bmp映射到bitmap
        public void BMP_to_map(int Pos_x, int Pos_y, Byte[] bmp, int height, int width)
        {
            Byte[] rows = new Byte[width];

            int loop = height / 8;
            if (height % 8 != 0) loop++;

            for (int page = 1; page <= loop; page++)
            {
                Array.Copy(bmp, width * (page - 1), rows, 0, width);

                for (int x_offset = 0; x_offset < width; x_offset++)
                {
                    Byte_ToMap(Pos_x + x_offset, Pos_y, rows[x_offset]);
                }
            }
        }
        //屏幕大小的byte数组更新到oled上
        public void RAM_Load(byte[] bmp, Bitmap oled)
        {

            byte[] line = new byte[OLED_Width]; //1 page 128*8
            List<Rectangle>
                PointList = new List<Rectangle>();
            PointList.Clear();

            OLED_BYTE oled_points;

            int loop = OLED_Height / 8;

            if (bmp.Length != OLED_Height * OLED_Width / 8) return;

            if (OLED_Height % 8 != 0) loop++;

            for (int page = 1, y = 0; page <= loop; page++, y += 8)
            {
                Array.Copy(bmp, OLED_Width * (page - 1), line, 0, OLED_Width);

                for (int x = 0; x < OLED_Width; x++)
                {
                    oled_points = new OLED_BYTE(x, y, line[x], Bit_Order);
                    PointList.AddRange(VH_GetPoints(oled_points));
                }
            }

            Rectangle[] points = PointList.ToArray();

            if (points.Length != 0)
            {
                Graphics Graphic = Graphics.FromImage(oled);
                Brush brush = new SolidBrush(Color.FromArgb(255, 255, 255));
                Clear(oled);
                Graphic.FillRectangles(brush, points);
            }
        }
        //显示bmp
        public void ShowBMP(int Pos_x, int Pos_y, int width, int height, byte[] bmp)
        {
            //为什么要先映射到map上显示的时候又转成bmp呢
            //为了更真实的模拟oled中的 列行式 行列式扫描

            byte[] oneline = new byte[width]; //屏幕一行buff
            int loop = height / 8;

            if (Pos_x >= OLED_Width || Pos_y >= OLED_Height) return;

            if (height % 8 != 0) loop++;

            for (int page = 1, y = 0; page <= loop; page++, y += 8)
            {
                Array.Copy(bmp, width * (page - 1), oneline, 0, width);
                for (int x = 0; x < width; x++)
                {   //一行BMP的字节数据映射到指定位置的oled_map(点阵)上
                    Byte_ToMap(Pos_x, Pos_y, oneline[x], x);
                }
            }
        }
        //显示bmp
        public void ShowBMP_with_Prop(int Pos_x, int Pos_y, Byte[] bmp, int height, int width, Bitmap oled)
        {
            //bmp 数组映射到 bitmap 
            BMP_to_map(Pos_x, Pos_y, bmp, height, width);

            MapTo_oled(oled);
        }

        #endregion

        #region bitmap 方式 底层模拟 暂未用 or 废弃代码
        #region 
        public void Map_To_Pixel1(Bitmap oled)
        {
            Graphics Graphic = Graphics.FromImage(oled);
            int cnt = 0, size = OLED_Height * OLED_Width;

            for (int V = 0; V < OLED_Height; V++)
            {
                for (int H = 0; H < OLED_Width; H++)
                    if (oled_map[V, H] == true) cnt++;
            }

            bool write_bool = true;
            Color color = Color.Black;

            if (cnt > size / 2)
            {
                Graphic.Clear(Color.White);
                write_bool = false;
            }
            else
            {
                Graphic.Clear(Color.Black);
                color = Color.White;
            }

            for (int V = 0; V < OLED_Height; V++)
            {
                for (int H = 0; H < OLED_Width; H++)
                {

                    if (oled_map[V, H] == write_bool)
                    {
                        Bitmap_point(H, V, oled, color);
                    }
                }
            }
        }

        // 利用bitmap 绘图速度更佳
        private  void Bitmap_point(int x, int y, Bitmap oled)
        {
            Bitmap_point(x, y, oled, Color.White);
        }

        // bitmap 行列扫描 绘制点图
        private  bool[] GetPoints_HV_Scan(int x, int y, Byte data)
        {
            bool[] oled_Matrix = new bool[0];

            oled_Matrix = Byte_order.Bool_Array(data, Bit_Order);

            return oled_Matrix;
        }
        private  bool[] GetPoints_HV_Scan(int x, int y, Byte data, Bitmap oled)
        {
            bool[] oled_Matrix = new bool[0];

            if (x >= OLED_Width || 8 + y >= OLED_Height) return oled_Matrix;

            oled_Matrix = Byte_order.Bool_Array(data, Bit_Order);

            return oled_Matrix;
        }

        // bitmap 水平/垂直扫描 绘制点图
        private  bool[] GetPoints_Linear_Scan(int x, int y, Byte data)
        {

            bool[] draw = Byte_order.Bool_Array(data, Bit_Order);
            bool[] oled_Matrix = new bool[8];

            return oled_Matrix;
        }
        private  bool[] GetPoints_Linear_Scan(int x, int y, Byte data, Bitmap oled)
        {

            bool[] draw = Byte_order.Bool_Array(data, Bit_Order);
            bool[] oled_Matrix = new bool[8];

            return oled_Matrix;
        }
        #endregion


        #endregion

        #region 顶层驱动 仅做map 不更新显示

        #region 内部调用
        private void HV_Mode(int x, int y, byte data)
        {
            if (x >= OLED_Width || y >= OLED_Height) return;

            bool[] points = Byte_order.Bool_Array(data, Bit_Order);

            if (points.Length <= 0) return;

            for (int offset = 0; offset < 8; offset++)
            {
                oled_map[y, x + offset] = points[offset];
            }
        }
        private void VH_Mode(int x, int y, byte data)
        {
            if (x >= OLED_Width || y >= OLED_Height) return;

            bool[] points = Byte_order.Bool_Array(data, Bit_Order);

            if (points.Length <= 0) return;

            for (int offset = 0; offset < 8; offset++)
            {
                //映射此点信息到buffer
                oled_map[y + offset, x] = points[offset];
            }
        }
        private void DrawByte(int x, int y, byte data)// 绘制一个字节数据
        {
            switch (Scan_Mode)
            {
                //case Oled_Mode.Vertical_scan:
                //    break;
                //case Oled_Mode.Horizontal_scan:
                //    break;
                case OLED_Scan_Mode.VertiByte_HoriScan:
                    
                    VH_Mode(x, y, data);// 列行式
                    break;
                case OLED_Scan_Mode.HoriByte_Vertiscan:
                    HV_Mode(x, y, data);// 行列式
                    break;
                default:
                    break;
            }
        }
        private void MapChar(int x, int y, byte asiic, int size, bool show)
        {
            int index = asiic - ' ';
            switch (size)
            {
                case 6:
                    {
                        index *= 6;//F6X8
                        for (int i = 0; i < 6; i++)
                        {
                            DrawByte(x + i, y, Fonts.F6X8[index + i]);
                        }
                        break;
                    }
                case 8:
                    {
                        index *= 8;
                        for (int i = 0; i < 6; i++)
                        {
                            //DrawByte(x + i, y, Fonts.F6X8[index + i]);
                        }
                        break;
                    }
            }
            if(show == true)
            {
                //ShowMap();
            }
        }
        #endregion

        public void ShowChar(int x, int y, byte asiic, int size)//显示一个字符
        {
            MapChar(x, y, asiic, size, true);
        }
        public void ShowString(int x, int y, string text, int size)//显示一个字符串
        {
            Byte[] data = Encoding.Default.GetBytes(text);
            for (int loop = 0; loop < text.Length; loop++)
            {
                MapChar(x, y, data[loop], size, false);
                x += (size);
            }
            //ShowMap();
        }
        public void Clear()
        {
            oled_map = new bool[OLED_Height/OLED_DPI, OLED_Width/ OLED_DPI];
        }
        #endregion

        #region  OLED屏幕参数设置
        //设置屏幕缓冲区大小
        private  void Set_Screen_Buff(int height, int width)
        {
            //oled_Screen_buff1 = new Rectangle[height, width];
            //oled_Screen = oled_Screen_buff1;
            oled_map = new bool[height, width];
        }
        //设置像素点大小 适应屏幕
        private  void Set_DPI(int height, int width)
        {
            OLED_Height = height;
            OLED_Width = width;

            OLED_DPI = 3;
            if (OLED_Height < 64 || OLED_Width < 128)
            {
                OLED_DPI++;
            }
        }
        //设置窗口大小 分辨率越低，尽量调大窗口
        public  void Set_PaintPanel(ref int height, ref int width)
        {
            if (height % 8 != 0)
            {
                height = (height / 8 + 1) * 8;
            }
            if (width % 8 != 0)
            {
                width = (width / 8 + 1) * 8;
            }
            Set_DPI(height, width);
            Set_Screen_Buff(height, width);

            height *= OLED_DPI;
            width *= OLED_DPI;
        }
        #endregion

    }
}
